﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Neo4jRestSharp.Neo4jObjectServices;
using Neo4jRestSharp.Neo4jRestServices;
using Newtonsoft.Json.Linq;

namespace Neo4jRestSharp
{
	public abstract class PropertyContainer
	{
		private Dictionary<string, string> properties;

		protected NodeObjectServices nodeObjectServices;
		protected JObject jsonObj;
		protected int nodeId;
		protected Uri selfUri;

		protected internal PropertyContainer()
		{}

		internal void InitServices(Uri uri)
		{
			Debug.Assert(nodeObjectServices == null);
			nodeObjectServices = ServicesFactory.ObjectServices(uri);
		}

		internal void InitWithJson(JObject jObject)
		{
			Debug.Assert(nodeObjectServices != null);
			this.jsonObj = jObject;
			ParseJson();
		}

		protected virtual void ParseJson()
		{
			Debug.Assert(nodeObjectServices != null);

			//get uri and id
			this.selfUri = new Uri(jsonObj["self"].Value<string>());
			string id = this.selfUri.Segments[this.selfUri.Segments.Length - 1];
			this.nodeId = int.Parse(id);

			//set data properties
			JObject dataToken = jsonObj["data"].Value<JObject>();
			foreach (var prop in dataToken)
			{
				this.SetProperty(prop.Key, prop.Value.Value<string>());
			}
		}

		internal void ClearForDelete()
		{
			Debug.Assert(nodeObjectServices != null);
			this.nodeId = 0;
			this.selfUri = null;
			this.jsonObj = null;
			this.nodeObjectServices = null;
			this.properties = null;
		}

		public int Id
		{
			get { return this.nodeId; }
		}

		public Uri SelfUri
		{
			get
			{
				Debug.Assert(nodeObjectServices != null);
				return this.selfUri;
			}
		}

		protected Dictionary<string, string> InternalProperties
		{
			get
			{
				if (properties == null)
				{
					properties = new Dictionary<string, string>();
					Node node = this as Node;
					if (node != null)
					{
						nodeObjectServices.GetNodeProperties(node);						
					}
					else
					{
						Relationship relationship = this as Relationship;
						if (relationship != null)
						{
							nodeObjectServices.GetRelationshipProperties(relationship);
						}
						else
						{
							throw new Neo4jException("Invalid PropertyContainer");
						}
					}					
				}
				return properties;
			}
		}

		protected internal void ClearLocalProperties()
		{
			properties = null;
		}

		public bool HasProperty(string key)
		{
			Debug.Assert(nodeObjectServices != null);
			return InternalProperties.ContainsKey(key);
		}

		public string GetProperty(string key)
		{
			Debug.Assert(nodeObjectServices != null);
			return InternalProperties[key];
		}

		public string TryGetProperty(string key, string defalutValue)
		{
			Debug.Assert(nodeObjectServices != null);
			string value;
			if (InternalProperties.TryGetValue(key, out value))
			{
				return value;
			}
			else
			{
				return defalutValue;
			}
		}

		public IEnumerable<KeyValuePair<string, string>> GetProperties()
		{
			Debug.Assert(nodeObjectServices != null);
			foreach (var property in InternalProperties)
			{
				yield return property;
			}
		}

		public void SetProperty(string key, string value)
		{
			Debug.Assert(nodeObjectServices != null);
			InternalProperties[key] = value;
		}

		public string RemoveProperty(string key)
		{
			Debug.Assert(nodeObjectServices != null);
			string value;
			if (InternalProperties.TryGetValue(key, out value))
			{
				InternalProperties.Remove(key);
				return value;
			}
			return null;
		}

		public void SaveProperties()
		{
			Debug.Assert(nodeObjectServices != null);

			Node node = this as Node;
			if (node != null)
			{
				nodeObjectServices.SetNodeProperties(node);
			}
			else
			{
				Relationship relationship = this as Relationship;
				if (relationship != null)
				{
					nodeObjectServices.SetRelationshipProperties(relationship);
				}
				else
				{
					throw new Neo4jException("Invalid PropertyContainer");
				}
			}	
			
			properties = null;
		}
	}
}
